#include "XSSLCtx.h"
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <iostream>
using namespace std;
// 验证回调函数
static int VerifyCallback(int preverify_ok, X509_STORE_CTX *x509_ctx){
    if(preverify_ok==0){
        cout<<"preverify_ok is error"<<endl;
    }else{
        cout<<"preverify_ok is ok"<<endl;
    }
    return preverify_ok;
}

/**
 * 初始化SSL上下文用于服务器
 * 
 * @param crt_file 服务器证书文件路径
 * @param key_file 服务器私钥文件路径
 * @param ca_file CA证书文件路径
 * 
 * @return 成功返回true，失败返回false
 * 
 * 此函数负责初始化SSL上下文，用于服务器端SSL连接的配置
 * 它会加载服务器的证书和私钥，并进行有效性检查
 */
bool XSSLCtx::InitServer(const char *crt_file, const char *key_file, const char *ca_file)
{
    // 创建SSL上下文
    ssl_ctx_=SSL_CTX_new(TLS_server_method());
    if(!ssl_ctx_){
        cerr<<"ssl_ctx new failed"<<endl;
        ERR_print_errors_fp(stderr);
        return false;
    }
    // 加载服务器证书
    if (SSL_CTX_use_certificate_file(ssl_ctx_, crt_file, SSL_FILETYPE_PEM) <= 0)
    {
        cerr<<"SSL_CTX_use_certificate_file failed"<<endl;
        ERR_print_errors_fp(stderr);
        return false;
    }
    // 加载服务器私钥
    if (SSL_CTX_use_PrivateKey_file(ssl_ctx_, key_file, SSL_FILETYPE_PEM) <= 0)
    {
        cerr<<"SSL_CTX_use_PrivateKey_file failed"<<endl;
        ERR_print_errors_fp(stderr);
        return false;
    }
    // 检查私钥与证书是否匹配
    if (SSL_CTX_check_private_key(ssl_ctx_) <= 0)
    {
        cerr<<"SSL_CTX_use_PrivateKey_file failed"<<endl;
        ERR_print_errors_fp(stderr);
        return false;
    }
    SetVerify(ca_file);
    // 所有配置成功完成，返回true
    return true;
}

XSSLCtx::XSSLCtx()
{
    OpenSSL_add_ssl_algorithms();
    /*为打印调试信息作准备*/
    SSL_load_error_strings();
}

XSSLCtx::~XSSLCtx()
{
}

/**
 * 创建一个新的XSSL对象并与其关联一个socket
 * 
 * @param socket 要关联的socket描述符
 * @return 返回一个XSSL对象，如果创建失败或参数无效，则返回默认构造的XSSL对象
 * 
 * 此函数首先检查传入的socket描述符是否有效，以及ssl_ctx_成员变量是否已初始化
 * 如果检查失败，函数将返回一个默认构造的XSSL对象，而不进行任何进一步的操作
 * 如果检查通过，函数将使用ssl_ctx_创建一个新的SSL对象，并将其与传入的socket描述符关联
 * 最后，将新创建的SSL对象赋给xssl，并返回xssl对象
 */
XSSL XSSLCtx::NewXSSL(int socket)
{
    XSSL xssl;
    // 检查socket描述符是否有效以及ssl_ctx_是否已初始化
    if(socket<=0||!ssl_ctx_){
        return xssl;
    }
    // 创建一个新的SSL对象
    auto ssl=SSL_new(ssl_ctx_);
    // 检查SSL对象是否成功创建
    if(!ssl){
        return xssl;
    }
    // 将SSL对象与socket描述符关联
    SSL_set_fd(ssl,socket);
    // 将新创建的SSL对象赋给xssl
    xssl.set_ssl_(ssl);
    // 返回包含新SSL对象的xssl
    return xssl;
}

/**
 * 初始化客户端SSL上下文
 * 
 * 该函数通过调用OpenSSL库的TLS_client_method函数来创建一个新的SSL上下文对象
 * 它用于后续的SSL通信初始化如果无法成功创建SSL上下文，则返回false
 * 
 * @param ca_file 包含信任的CA证书的文件路径用于验证服务器证书的有效性
 * @return 成功初始化SSL上下文返回true，否则返回false
 */
bool XSSLCtx::InitClient(const char *ca_file)
{
    // 创建新的SSL上下文，适用于客户端
    ssl_ctx_ = SSL_CTX_new(TLS_client_method());
    // 检查是否成功创建SSL上下文
    if(!ssl_ctx_){
        return false;
    }
    // 设置证书验证相关配置
    SetVerify(ca_file);
    return true;
}

void XSSLCtx::Close()
{
    if(ssl_ctx_){
        SSL_CTX_free(ssl_ctx_);
        ssl_ctx_=nullptr;
    }
}

/**
 * 设置SSL上下文的验证选项
 * @param ca_file 包含可信CA证书的文件路径
 * 
 * 此函数配置SSL上下文以验证对等实体的证书，并指定用于验证的CA证书文件
 * 如果ca_file参数为空，则不进行任何操作
 */
void XSSLCtx::SetVerify(const char *ca_file)
{
    // 如果CA证书文件路径为空，则不执行任何操作并返回
    if(!ca_file||!ssl_ctx_||!strlen(ca_file))return;

    // 配置SSL上下文以验证对等实体的证书，并使用自定义的验证回调函数
    SSL_CTX_set_verify(ssl_ctx_, SSL_VERIFY_PEER, VerifyCallback);

    // 加载指定路径下的CA证书文件，用于验证对等实体的证书
    SSL_CTX_load_verify_locations(ssl_ctx_, ca_file, NULL);
}